package com.zlyx.easy.database.supports;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.util.List;

import org.springframework.util.StringUtils;

import com.zlyx.easy.core.map.CommonMap;
import com.zlyx.easy.core.map.ObjectMap;
import com.zlyx.easy.core.tool.EasyBuffer;
import com.zlyx.easy.core.tool.Ops;
import com.zlyx.easy.core.utils.DateUtils;
import com.zlyx.easy.core.utils.FieldUtils;
import com.zlyx.easy.core.utils.LogUtils;
import com.zlyx.easy.core.utils.ObjectUtils;
import com.zlyx.easy.core.utils.SpringUtils;
import com.zlyx.easy.database.DaoMarkerFactory;
import com.zlyx.easy.database.annotations.Condition;
import com.zlyx.easy.database.annotations.ForeignKey;
import com.zlyx.easy.database.annotations.Match;
import com.zlyx.easy.database.annotations.Table;
import com.zlyx.easy.database.enums.MatchType;
import com.zlyx.easy.database.enums.SqlType;
import com.zlyx.easy.database.utils.TableUtils;

/**
 * @Auth 赵光
 * @Describle
 * @2019年1月13日 下午4:20:38
 */
public class SqlFactory {

	protected Class<?> tableClass;

	protected String tableName;

	/**
	 * 存放构成Sql语句的不同部分
	 */
	protected CommonMap sqlElements = CommonMap.newMap();

	/**
	 * 存放Sql条件片段(没有参数状态)
	 */
	protected CommonMap fragments = CommonMap.newMap();

	/**
	 * 存放成型的Sql条件片段(含参数)
	 */
	protected EasyBuffer sqlCondition = EasyBuffer.newBuffer();

	public SqlFactory(String tableName) {
		this.tableName = tableName;
		String tableAlias = TableUtils.getAlias(tableName);
		this.sqlElements.addValue(SqlSupport.TYPENAME, SqlType.Select.toString()).addValue(SqlSupport.TABLENAME,
				tableName + " " + tableAlias);
		this.tableClass = SpringUtils.getBean(tableName).getClass();
	}

	/**
	 * 设置结果集
	 * 
	 * @param fields
	 * @return
	 * @throws Exception
	 */
	public boolean tables(String[] results, Table... tables) throws Exception {
		EasyBuffer eb = EasyBuffer.newBuffer(spliceResults(tableName, results)).append(", ");
		Field field = null;
		ForeignKey key = null;
		for (Table table : tables) {
			if (tables.length != 1 && table.results().length > 0 && StringUtils.isEmpty(table.tableName())) {
				LogUtils.err(this.getClass(), "Please check and set an tableName for your sql!");
				return false;
			}
			if (ObjectUtils.isNotEmpty(table.tableName())) {
				field = SqlHelper.getForeginKey(tableClass, table.tableName());
				if (ObjectUtils.isEmpty(field)) {
					LogUtils.warn(this.getClass(), EasyBuffer.wrapper(tableName, "在", table.tableName(), "中找不到对应的外键!"));
					return false;
				}
				key = field.getAnnotation(ForeignKey.class);
				eb.append(spliceResults(table.tableName(), table.results())).append(", ");
				String tableAlias = TableUtils.getAlias(table.tableName());
				sqlCondition.append(" left join ").append(table.tableName()).append(" ").append(tableAlias).append(" ")
						.append("on ").append(tableName).append(".").append(DaoMarkerFactory.getFieldName(field))
						.append(" = ").append(tableAlias).append(".").append(key.key());
			}
		}
		sqlElements.put(SqlSupport.RESULTS, eb.clearEnd());
		return true;
	}

	/**
	 * 将结果集数组打包为成型以","分割字符串
	 * 
	 * @param tableName
	 * @param fields
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 */
	private String spliceResults(String tableName, String... fields) throws Exception {
		if (fields == null) {
			return "";
		}
		String alias = TableUtils.getAlias(tableName);
		Class<?> table = SpringUtils.getBeanClass(tableName);
		EasyBuffer results = EasyBuffer.newBuffer();
		if ((fields.length == 1 && Table.FILED_ALL.equals(fields[0])) || fields.length == 0) {
			List<Field> fieldList = FieldUtils.getClassFields(table);
			boolean isStatic = false;
			for (Field field : fieldList) {
				isStatic = Modifier.isStatic(field.getModifiers());
				if (!isStatic && field.getType().toString().contains("java")) {
					results.append(alias).append(".").append(DaoMarkerFactory.getFieldName(field)).append(" as ")
							.append(alias).append("_").append(field.getName()).append(",");
				}

			}
		} else {
			for (String field : fields) {
				results.append(alias).append(".").append(field).append(" as ").append(alias).append("_")
						.append(DaoMarkerFactory.getFieldName(table.getField(field))).append(", ");
			}
		}
		return results.clearEnd();
	}

	/**
	 * 添加sql条件
	 * 
	 * @param conditions
	 */
	public void addCondition(ObjectMap paramsMap, String... conditions) {
		sqlCondition.append(" and (");
		for (String condition : conditions) {
			if (condition.contains(" and") || condition.contains(" or")) {
				if (conditions.length > 1) {
					condition = EasyBuffer.wrapper(" (", condition, ")");
				}
			}
			if (!StringUtils.isEmpty(condition)) {
				sqlCondition.append(condition).append(" or ");
			}
		}
		if (sqlCondition.endsWith(" or ")) {
			sqlCondition.delete(3);
		}
		sqlCondition.append(")");
		if (sqlCondition.endsWith(" and ()")) {
			sqlCondition.delete(7);
		}
		replaceCondition(paramsMap);
	}

	private void replaceCondition(ObjectMap paramsMap) {
		String value = null;
		for (String fieldName : paramsMap.keySet()) {
			value = EasyBuffer.valueOf(paramsMap.get(fieldName));
			if (ObjectUtils.isNotEmpty(value)) {
				sqlCondition.replace("#{" + fieldName + "}", value).replace("${" + fieldName + "}", value);
			}
		}
	}

	/**
	 * where条件开始
	 * 
	 * @return
	 */
	public void where() {
		sqlCondition.append(SqlSupport.WHERE);
	}

	/**
	 * 设置sql内容
	 * 
	 * @param content
	 * @return
	 */
	public SqlFactory content(String content) {
		sqlElements.addValue(SqlSupport.CONTENT, content);
		return this;
	}

	public void start() {
		if (!sqlCondition.endsWith(SqlSupport.WHERE)) {
			sqlCondition.append(" and ", SqlSupport.HEAD);
		}
	}

	public void end() {
		if (sqlCondition.endsWith(SqlSupport.WHERE)) {
			sqlCondition.replace(SqlSupport.WHERE, "");
		}
		if (sqlCondition.endsWith(SqlSupport.WHERE + " and ")) {
			sqlCondition.replace(SqlSupport.WHERE + " and ", "");
		}
		if (sqlCondition.endsWith(" or ")) {
			sqlCondition.delete(4);
		}
		if (sqlCondition.endsWith(" and ")) {
			sqlCondition.delete(5);
		}
		sqlCondition.replace(SqlSupport.AND, "").replace("and ()", "").replace("1 == 1  and", "")
				.replace("1 == 1 and", "").replace(SqlSupport.OR, "").replace("or ()", "").replace("1 == 1  or", "")
				.replace("1 == 1 or", "").replace("  ", " ");
		if (sqlCondition.contains(SqlSupport.HEAD)) {
			sqlCondition.replace(SqlSupport.HEAD, "(").append(SqlSupport.END);
		}

	}

	/**
	 * and占位符
	 * 
	 * @return
	 */
	public SqlFactory and() {
		if (!sqlCondition.endsWith(SqlSupport.WHERE) && !sqlCondition.endsWith(SqlSupport.HEAD)) {
			sqlCondition.append(" and (${AND})");
		} else if (sqlCondition.endsWith(SqlSupport.WHERE)) {
			sqlCondition.append(" and ", SqlSupport.PREFIX);
		}
		return this;
	}

	/**
	 * or占位符
	 * 
	 * @return
	 */
	public SqlFactory or() {
		if (!sqlCondition.endsWith(SqlSupport.WHERE) && !sqlCondition.endsWith(SqlSupport.HEAD)) {
			sqlCondition.append(" or (${OR})");
		} else if (sqlCondition.endsWith(SqlSupport.WHERE)) {
			sqlCondition.append(" and ", SqlSupport.PREFIX);
		}
		return this;
	}

	/**
	 * 生成不包含值的参数子句
	 * 
	 * @param tableName
	 * @param strings
	 * @return
	 * @throws Exception
	 */
	public SqlFactory fields(Condition condition, String tableName) throws Exception {
		if (Ops.isNotEmpty(condition.tableName())) {
			tableName = condition.tableName();
		}
		if (ObjectUtils.isEmpty(tableName)) {
			return this;
		}
		Field field = null;
		Match match = null;
		MatchType matchType = null;
		String key = null;
		String alias = TableUtils.getAlias(tableName);
		for (String fieldName : condition.fields()) {
			if (!StringUtils.isEmpty(fieldName) && fragments.withoutKey(fieldName)) {
				field = tableClass.getDeclaredField(fieldName);
				if (field != null) {
					match = field.getAnnotation(Match.class);
					matchType = MatchType.EQUAL;
					if (!ObjectUtils.isEmpty(match)) {
						matchType = match.match();
					}
					key = DaoMarkerFactory.getFieldName(field);
					if (ObjectUtils.isEmpty(key)) {
						key = fieldName;
					}
					fragments.put(fieldName, matchType.getSymbol().replace("@{s}", alias).replace("${s}", key));
				}
			}
		}
		return this;
	}

	/**
	 * 为参数子句赋值并组装到SQL条件片段中
	 * 
	 * @param paramsMap
	 * @return
	 */
	public SqlFactory values(ObjectMap paramsMap) {
		try {
			int isComposite = 0;
			EasyBuffer eb = EasyBuffer.newBuffer();
			Object value = null;
			for (String fieldName : paramsMap.keySet()) {
				value = paramsMap.get(fieldName);
				if (ObjectUtils.isNotEmpty(value)) {
					if (fragments.containsKey(fieldName)) {
						++isComposite;
						eb.append(getFragment(fieldName, value)).append(" and");
					}
					fragments.remove(fieldName);
				}
			}
			eb.delete(3);
			String sql = eb.toString();
			if (isComposite > 1) {
				sql = EasyBuffer.wrapper("(", sql, ")");
			}
			if (sqlCondition.contains(SqlSupport.AND) || sqlCondition.contains(SqlSupport.OR)
					|| sqlCondition.contains(SqlSupport.PREFIX)) {
				sqlCondition.replace(SqlSupport.AND, sql);
				sqlCondition.replace(SqlSupport.OR, sql);
				sqlCondition.replace(SqlSupport.PREFIX, sql);
			} else if (sqlCondition.endsWith(SqlSupport.HEAD)) {
				sqlCondition.append(sql);
			} else if (sqlCondition.endsWith(SqlSupport.WHERE)) {
				sqlCondition.append(" and", sql);
			} else {
				sqlCondition.append(" and", sql);
			}
		} catch (Exception e) {
			LogUtils.err(e);
		}
		return this;
	}

	/**
	 * 为参数子句赋值
	 * 
	 * @param fieldName
	 * @param value
	 * @return
	 */
	private String getFragment(String fieldName, Object value) {
		if (String.class == value.getClass()) {
			value = EasyBuffer.wrapper("'", value, "'");
		}
		if (Date.class.isAssignableFrom(value.getClass())) {
			value = DateUtils.format(value);
		}
		return fragments.get(fieldName).replace("#{s}", String.valueOf(value));
	}

	/**
	 * 标记一个sql语句结束并返回成型的sql语句
	 * 
	 * @return
	 */
	public String assemble(Object target) {
		sqlElements.put(SqlSupport.CONDITON, sqlCondition.toString());
		return SqlAssembler.assemble(target, sqlElements);
	}

	@Override
	public String toString() {
		return assemble(this);
	}
}
